// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>

#include <vector>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_test.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_WIN)
#include "base/message_loop/message_pump_win.h"
#include "base/process/memory.h"
#include "base/strings/string16.h"
#include "base/win/current_module.h"
#include "base/win/scoped_handle.h"
#endif

namespace base {

// TODO(darin): Platform-specific MessageLoop tests should be grouped together
// to avoid chopping this file up with so many #ifdefs.

namespace {

    std::unique_ptr<MessagePump> TypeDefaultMessagePumpFactory()
    {
        return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT);
    }

    std::unique_ptr<MessagePump> TypeIOMessagePumpFactory()
    {
        return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO);
    }

    std::unique_ptr<MessagePump> TypeUIMessagePumpFactory()
    {
        return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI);
    }

    class Foo : public RefCounted<Foo> {
    public:
        Foo()
            : test_count_(0)
        {
        }

        void Test1ConstRef(const std::string& a)
        {
            ++test_count_;
            result_.append(a);
        }

        int test_count() const { return test_count_; }
        const std::string& result() const { return result_; }

    private:
        friend class RefCounted<Foo>;

        ~Foo() { }

        int test_count_;
        std::string result_;
    };

#if defined(OS_WIN)

    // This function runs slowly to simulate a large amount of work being done.
    static void SlowFunc(TimeDelta pause, int* quit_counter)
    {
        PlatformThread::Sleep(pause);
        if (--(*quit_counter) == 0)
            MessageLoop::current()->QuitWhenIdle();
    }

    // This function records the time when Run was called in a Time object, which is
    // useful for building a variety of MessageLoop tests.
    static void RecordRunTimeFunc(Time* run_time, int* quit_counter)
    {
        *run_time = Time::Now();

        // Cause our Run function to take some time to execute.  As a result we can
        // count on subsequent RecordRunTimeFunc()s running at a future time,
        // without worry about the resolution of our system clock being an issue.
        SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
    }

    void SubPumpFunc()
    {
        MessageLoop::current()->SetNestableTasksAllowed(true);
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        MessageLoop::current()->QuitWhenIdle();
    }

    void RunTest_PostDelayedTask_SharedTimer_SubPump()
    {
        MessageLoop loop(MessageLoop::TYPE_UI);

        // Test that the interval of the timer, used to run the next delayed task, is
        // set to a value corresponding to when the next delayed task should run.

        // By setting num_tasks to 1, we ensure that the first task to run causes the
        // run loop to exit.
        int num_tasks = 1;
        Time run_time;

        loop.PostTask(FROM_HERE, Bind(&SubPumpFunc));

        // This very delayed task should never run.
        loop.PostDelayedTask(
            FROM_HERE,
            Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
            TimeDelta::FromSeconds(1000));

        // This slightly delayed task should run from within SubPumpFunc.
        loop.PostDelayedTask(
            FROM_HERE,
            Bind(&PostQuitMessage, 0),
            TimeDelta::FromMilliseconds(10));

        Time start_time = Time::Now();

        loop.Run();
        EXPECT_EQ(1, num_tasks);

        // Ensure that we ran in far less time than the slower timer.
        TimeDelta total_time = Time::Now() - start_time;
        EXPECT_GT(5000, total_time.InMilliseconds());

        // In case both timers somehow run at nearly the same time, sleep a little
        // and then run all pending to force them both to have run.  This is just
        // encouraging flakiness if there is any.
        PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
        RunLoop().RunUntilIdle();

        EXPECT_TRUE(run_time.is_null());
    }

    const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test";

    enum TaskType {
        MESSAGEBOX,
        ENDDIALOG,
        RECURSIVE,
        TIMEDMESSAGELOOP,
        QUITMESSAGELOOP,
        ORDERED,
        PUMPS,
        SLEEP,
        RUNS,
    };

    // Saves the order in which the tasks executed.
    struct TaskItem {
        TaskItem(TaskType t, int c, bool s)
            : type(t)
            , cookie(c)
            , start(s)
        {
        }

        TaskType type;
        int cookie;
        bool start;

        bool operator==(const TaskItem& other) const
        {
            return type == other.type && cookie == other.cookie && start == other.start;
        }
    };

    std::ostream& operator<<(std::ostream& os, TaskType type)
    {
        switch (type) {
        case MESSAGEBOX:
            os << "MESSAGEBOX";
            break;
        case ENDDIALOG:
            os << "ENDDIALOG";
            break;
        case RECURSIVE:
            os << "RECURSIVE";
            break;
        case TIMEDMESSAGELOOP:
            os << "TIMEDMESSAGELOOP";
            break;
        case QUITMESSAGELOOP:
            os << "QUITMESSAGELOOP";
            break;
        case ORDERED:
            os << "ORDERED";
            break;
        case PUMPS:
            os << "PUMPS";
            break;
        case SLEEP:
            os << "SLEEP";
            break;
        default:
            NOTREACHED();
            os << "Unknown TaskType";
            break;
        }
        return os;
    }

    std::ostream& operator<<(std::ostream& os, const TaskItem& item)
    {
        if (item.start)
            return os << item.type << " " << item.cookie << " starts";
        else
            return os << item.type << " " << item.cookie << " ends";
    }

    class TaskList {
    public:
        void RecordStart(TaskType type, int cookie)
        {
            TaskItem item(type, cookie, true);
            DVLOG(1) << item;
            task_list_.push_back(item);
        }

        void RecordEnd(TaskType type, int cookie)
        {
            TaskItem item(type, cookie, false);
            DVLOG(1) << item;
            task_list_.push_back(item);
        }

        size_t Size()
        {
            return task_list_.size();
        }

        TaskItem Get(int n)
        {
            return task_list_[n];
        }

    private:
        std::vector<TaskItem> task_list_;
    };

    // MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
    // common controls (like OpenFile) and StartDoc printing function can cause
    // implicit message loops.
    void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant)
    {
        order->RecordStart(MESSAGEBOX, cookie);
        if (is_reentrant)
            MessageLoop::current()->SetNestableTasksAllowed(true);
        MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
        order->RecordEnd(MESSAGEBOX, cookie);
    }

    // Will end the MessageBox.
    void EndDialogFunc(TaskList* order, int cookie)
    {
        order->RecordStart(ENDDIALOG, cookie);
        HWND window = GetActiveWindow();
        if (window != NULL) {
            EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
            // Cheap way to signal that the window wasn't found if RunEnd() isn't
            // called.
            order->RecordEnd(ENDDIALOG, cookie);
        }
    }

    void RecursiveFunc(TaskList* order, int cookie, int depth,
        bool is_reentrant)
    {
        order->RecordStart(RECURSIVE, cookie);
        if (depth > 0) {
            if (is_reentrant)
                MessageLoop::current()->SetNestableTasksAllowed(true);
            MessageLoop::current()->PostTask(
                FROM_HERE,
                Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
        }
        order->RecordEnd(RECURSIVE, cookie);
    }

    void QuitFunc(TaskList* order, int cookie)
    {
        order->RecordStart(QUITMESSAGELOOP, cookie);
        MessageLoop::current()->QuitWhenIdle();
        order->RecordEnd(QUITMESSAGELOOP, cookie);
    }

    void RecursiveFuncWin(MessageLoop* target,
        HANDLE event,
        bool expect_window,
        TaskList* order,
        bool is_reentrant)
    {
        target->PostTask(FROM_HERE,
            Bind(&RecursiveFunc, order, 1, 2, is_reentrant));
        target->PostTask(FROM_HERE,
            Bind(&MessageBoxFunc, order, 2, is_reentrant));
        target->PostTask(FROM_HERE,
            Bind(&RecursiveFunc, order, 3, 2, is_reentrant));
        // The trick here is that for recursive task processing, this task will be
        // ran _inside_ the MessageBox message loop, dismissing the MessageBox
        // without a chance.
        // For non-recursive task processing, this will be executed _after_ the
        // MessageBox will have been dismissed by the code below, where
        // expect_window_ is true.
        target->PostTask(FROM_HERE,
            Bind(&EndDialogFunc, order, 4));
        target->PostTask(FROM_HERE,
            Bind(&QuitFunc, order, 5));

        // Enforce that every tasks are sent before starting to run the main thread
        // message loop.
        ASSERT_TRUE(SetEvent(event));

        // Poll for the MessageBox. Don't do this at home! At the speed we do it,
        // you will never realize one MessageBox was shown.
        for (; expect_window;) {
            HWND window = FindWindow(L"#32770", kMessageBoxTitle);
            if (window) {
                // Dismiss it.
                for (;;) {
                    HWND button = FindWindowEx(window, NULL, L"Button", NULL);
                    if (button != NULL) {
                        EXPECT_EQ(0, SendMessage(button, WM_LBUTTONDOWN, 0, 0));
                        EXPECT_EQ(0, SendMessage(button, WM_LBUTTONUP, 0, 0));
                        break;
                    }
                }
                break;
            }
        }
    }

    // TODO(darin): These tests need to be ported since they test critical
    // message loop functionality.

    // A side effect of this test is the generation a beep. Sorry.
    void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type)
    {
        MessageLoop loop(message_loop_type);

        Thread worker("RecursiveDenial2_worker");
        Thread::Options options;
        options.message_loop_type = message_loop_type;
        ASSERT_EQ(true, worker.StartWithOptions(options));
        TaskList order;
        win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
        worker.message_loop()->PostTask(FROM_HERE,
            Bind(&RecursiveFuncWin,
                MessageLoop::current(),
                event.Get(),
                true,
                &order,
                false));
        // Let the other thread execute.
        WaitForSingleObject(event.Get(), INFINITE);
        MessageLoop::current()->Run();

        ASSERT_EQ(17u, order.Size());
        EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
        EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
        EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
        EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false));
        EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false));
        // When EndDialogFunc is processed, the window is already dismissed, hence no
        // "end" entry.
        EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true));
        EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true));
        EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false));
        EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true));
        EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false));
        EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false));
        EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true));
        EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false));
        EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false));
    }

    // A side effect of this test is the generation a beep. Sorry.  This test also
    // needs to process windows messages on the current thread.
    void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type)
    {
        MessageLoop loop(message_loop_type);

        Thread worker("RecursiveSupport2_worker");
        Thread::Options options;
        options.message_loop_type = message_loop_type;
        ASSERT_EQ(true, worker.StartWithOptions(options));
        TaskList order;
        win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
        worker.message_loop()->PostTask(FROM_HERE,
            Bind(&RecursiveFuncWin,
                MessageLoop::current(),
                event.Get(),
                false,
                &order,
                true));
        // Let the other thread execute.
        WaitForSingleObject(event.Get(), INFINITE);
        MessageLoop::current()->Run();

        ASSERT_EQ(18u, order.Size());
        EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
        EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
        EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
        // Note that this executes in the MessageBox modal loop.
        EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false));
        EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true));
        EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false));
        EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false));
        /* The order can subtly change here. The reason is that when RecursiveFunc(1)
     is called in the main thread, if it is faster than getting to the
     PostTask(FROM_HERE, Bind(&QuitFunc) execution, the order of task
     execution can change. We don't care anyway that the order isn't correct.
  EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true));
  EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false));
  EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
  EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
  */
        EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false));
        EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true));
        EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false));
        EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true));
        EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false));
    }

#endif // defined(OS_WIN)

    void PostNTasksThenQuit(int posts_remaining)
    {
        if (posts_remaining > 1) {
            MessageLoop::current()->task_runner()->PostTask(
                FROM_HERE, Bind(&PostNTasksThenQuit, posts_remaining - 1));
        } else {
            MessageLoop::current()->QuitWhenIdle();
        }
    }

#if defined(OS_WIN)

    class TestIOHandler : public MessageLoopForIO::IOHandler {
    public:
        TestIOHandler(const wchar_t* name, HANDLE signal, bool wait);

        void OnIOCompleted(MessageLoopForIO::IOContext* context,
            DWORD bytes_transfered,
            DWORD error) override;

        void Init();
        void WaitForIO();
        OVERLAPPED* context() { return &context_.overlapped; }
        DWORD size() { return sizeof(buffer_); }

    private:
        char buffer_[48];
        MessageLoopForIO::IOContext context_;
        HANDLE signal_;
        win::ScopedHandle file_;
        bool wait_;
    };

    TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait)
        : signal_(signal)
        , wait_(wait)
    {
        memset(buffer_, 0, sizeof(buffer_));

        file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED, NULL));
        EXPECT_TRUE(file_.IsValid());
    }

    void TestIOHandler::Init()
    {
        MessageLoopForIO::current()->RegisterIOHandler(file_.Get(), this);

        DWORD read;
        EXPECT_FALSE(ReadFile(file_.Get(), buffer_, size(), &read, context()));
        EXPECT_EQ(static_cast<DWORD>(ERROR_IO_PENDING), GetLastError());
        if (wait_)
            WaitForIO();
    }

    void TestIOHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
        DWORD bytes_transfered, DWORD error)
    {
        ASSERT_TRUE(context == &context_);
        ASSERT_TRUE(SetEvent(signal_));
    }

    void TestIOHandler::WaitForIO()
    {
        EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(300, this));
        EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(400, this));
    }

    void RunTest_IOHandler()
    {
        win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL));
        ASSERT_TRUE(callback_called.IsValid());

        const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe";
        win::ScopedHandle server(
            CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
        ASSERT_TRUE(server.IsValid());

        Thread thread("IOHandler test");
        Thread::Options options;
        options.message_loop_type = MessageLoop::TYPE_IO;
        ASSERT_TRUE(thread.StartWithOptions(options));

        MessageLoop* thread_loop = thread.message_loop();
        ASSERT_TRUE(NULL != thread_loop);

        TestIOHandler handler(kPipeName, callback_called.Get(), false);
        thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler)));
        // Make sure the thread runs and sleeps for lack of work.
        PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));

        const char buffer[] = "Hello there!";
        DWORD written;
        EXPECT_TRUE(WriteFile(server.Get(), buffer, sizeof(buffer), &written, NULL));

        DWORD result = WaitForSingleObject(callback_called.Get(), 1000);
        EXPECT_EQ(WAIT_OBJECT_0, result);

        thread.Stop();
    }

    void RunTest_WaitForIO()
    {
        win::ScopedHandle callback1_called(
            CreateEvent(NULL, TRUE, FALSE, NULL));
        win::ScopedHandle callback2_called(
            CreateEvent(NULL, TRUE, FALSE, NULL));
        ASSERT_TRUE(callback1_called.IsValid());
        ASSERT_TRUE(callback2_called.IsValid());

        const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1";
        const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2";
        win::ScopedHandle server1(
            CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
        win::ScopedHandle server2(
            CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
        ASSERT_TRUE(server1.IsValid());
        ASSERT_TRUE(server2.IsValid());

        Thread thread("IOHandler test");
        Thread::Options options;
        options.message_loop_type = MessageLoop::TYPE_IO;
        ASSERT_TRUE(thread.StartWithOptions(options));

        MessageLoop* thread_loop = thread.message_loop();
        ASSERT_TRUE(NULL != thread_loop);

        TestIOHandler handler1(kPipeName1, callback1_called.Get(), false);
        TestIOHandler handler2(kPipeName2, callback2_called.Get(), true);
        thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler1)));
        // TODO(ajwong): Do we really need such long Sleeps in this function?
        // Make sure the thread runs and sleeps for lack of work.
        TimeDelta delay = TimeDelta::FromMilliseconds(100);
        PlatformThread::Sleep(delay);
        thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, Unretained(&handler2)));
        PlatformThread::Sleep(delay);

        // At this time handler1 is waiting to be called, and the thread is waiting
        // on the Init method of handler2, filtering only handler2 callbacks.

        const char buffer[] = "Hello there!";
        DWORD written;
        EXPECT_TRUE(WriteFile(server1.Get(), buffer, sizeof(buffer), &written, NULL));
        PlatformThread::Sleep(2 * delay);
        EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
            WaitForSingleObject(callback1_called.Get(), 0))
            << "handler1 has not been called";

        EXPECT_TRUE(WriteFile(server2.Get(), buffer, sizeof(buffer), &written, NULL));

        HANDLE objects[2] = { callback1_called.Get(), callback2_called.Get() };
        DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000);
        EXPECT_EQ(WAIT_OBJECT_0, result);

        thread.Stop();
    }

#endif // defined(OS_WIN)

} // namespace

//-----------------------------------------------------------------------------
// Each test is run against each type of MessageLoop.  That way we are sure
// that message loops work properly in all configurations.  Of course, in some
// cases, a unit test may only be for a particular type of loop.

RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory);
RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory);
RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory);

#if defined(OS_WIN)
// Additional set of tests for GPU version of UI message loop.
RUN_MESSAGE_LOOP_TESTS(GPU, &MessagePumpForGpu::CreateMessagePumpForGpu);

TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump)
{
    RunTest_PostDelayedTask_SharedTimer_SubPump();
}

// This test occasionally hangs. See http://crbug.com/44567.
TEST(MessageLoopTest, DISABLED_RecursiveDenial2)
{
    RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT);
    RunTest_RecursiveDenial2(MessageLoop::TYPE_UI);
    RunTest_RecursiveDenial2(MessageLoop::TYPE_IO);
}

TEST(MessageLoopTest, RecursiveSupport2)
{
    // This test requires a UI loop.
    RunTest_RecursiveSupport2(MessageLoop::TYPE_UI);
}
#endif // defined(OS_WIN)

class DummyTaskObserver : public MessageLoop::TaskObserver {
public:
    explicit DummyTaskObserver(int num_tasks)
        : num_tasks_started_(0)
        , num_tasks_processed_(0)
        , num_tasks_(num_tasks)
    {
    }

    ~DummyTaskObserver() override { }

    void WillProcessTask(const PendingTask& pending_task) override
    {
        num_tasks_started_++;
        EXPECT_LE(num_tasks_started_, num_tasks_);
        EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1);
    }

    void DidProcessTask(const PendingTask& pending_task) override
    {
        num_tasks_processed_++;
        EXPECT_LE(num_tasks_started_, num_tasks_);
        EXPECT_EQ(num_tasks_started_, num_tasks_processed_);
    }

    int num_tasks_started() const { return num_tasks_started_; }
    int num_tasks_processed() const { return num_tasks_processed_; }

private:
    int num_tasks_started_;
    int num_tasks_processed_;
    const int num_tasks_;

    DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver);
};

TEST(MessageLoopTest, TaskObserver)
{
    const int kNumPosts = 6;
    DummyTaskObserver observer(kNumPosts);

    MessageLoop loop;
    loop.AddTaskObserver(&observer);
    loop.task_runner()->PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumPosts));
    RunLoop().Run();
    loop.RemoveTaskObserver(&observer);

    EXPECT_EQ(kNumPosts, observer.num_tasks_started());
    EXPECT_EQ(kNumPosts, observer.num_tasks_processed());
}

#if defined(OS_WIN)
TEST(MessageLoopTest, IOHandler)
{
    RunTest_IOHandler();
}

TEST(MessageLoopTest, WaitForIO)
{
    RunTest_WaitForIO();
}

TEST(MessageLoopTest, HighResolutionTimer)
{
    MessageLoop loop;
    Time::EnableHighResolutionTimer(true);

    const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
    const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);

    EXPECT_FALSE(loop.HasHighResolutionTasks());
    // Post a fast task to enable the high resolution timers.
    loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
        kFastTimer);
    EXPECT_TRUE(loop.HasHighResolutionTasks());
    loop.Run();
    EXPECT_FALSE(loop.HasHighResolutionTasks());
    EXPECT_FALSE(Time::IsHighResolutionTimerInUse());
    // Check that a slow task does not trigger the high resolution logic.
    loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
        kSlowTimer);
    EXPECT_FALSE(loop.HasHighResolutionTasks());
    loop.Run();
    EXPECT_FALSE(loop.HasHighResolutionTasks());
    Time::EnableHighResolutionTimer(false);
}

#endif // defined(OS_WIN)

#if defined(OS_POSIX) && !defined(OS_NACL)

namespace {

    class QuitDelegate : public MessageLoopForIO::Watcher {
    public:
        void OnFileCanWriteWithoutBlocking(int fd) override
        {
            MessageLoop::current()->QuitWhenIdle();
        }
        void OnFileCanReadWithoutBlocking(int fd) override
        {
            MessageLoop::current()->QuitWhenIdle();
        }
    };

    TEST(MessageLoopTest, FileDescriptorWatcherOutlivesMessageLoop)
    {
        // Simulate a MessageLoop that dies before an FileDescriptorWatcher.
        // This could happen when people use the Singleton pattern or atexit.

        // Create a file descriptor.  Doesn't need to be readable or writable,
        // as we don't need to actually get any notifications.
        // pipe() is just the easiest way to do it.
        int pipefds[2];
        int err = pipe(pipefds);
        ASSERT_EQ(0, err);
        int fd = pipefds[1];
        {
            // Arrange for controller to live longer than message loop.
            MessageLoopForIO::FileDescriptorWatcher controller;
            {
                MessageLoopForIO message_loop;

                QuitDelegate delegate;
                message_loop.WatchFileDescriptor(fd,
                    true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
                // and don't run the message loop, just destroy it.
            }
        }
        if (IGNORE_EINTR(close(pipefds[0])) < 0)
            PLOG(ERROR) << "close";
        if (IGNORE_EINTR(close(pipefds[1])) < 0)
            PLOG(ERROR) << "close";
    }

    TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop)
    {
        // Verify that it's ok to call StopWatchingFileDescriptor().
        // (Errors only showed up in valgrind.)
        int pipefds[2];
        int err = pipe(pipefds);
        ASSERT_EQ(0, err);
        int fd = pipefds[1];
        {
            // Arrange for message loop to live longer than controller.
            MessageLoopForIO message_loop;
            {
                MessageLoopForIO::FileDescriptorWatcher controller;

                QuitDelegate delegate;
                message_loop.WatchFileDescriptor(fd,
                    true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
                controller.StopWatchingFileDescriptor();
            }
        }
        if (IGNORE_EINTR(close(pipefds[0])) < 0)
            PLOG(ERROR) << "close";
        if (IGNORE_EINTR(close(pipefds[1])) < 0)
            PLOG(ERROR) << "close";
    }

} // namespace

#endif // defined(OS_POSIX) && !defined(OS_NACL)

namespace {
    // Inject a test point for recording the destructor calls for Closure objects
    // send to MessageLoop::PostTask(). It is awkward usage since we are trying to
    // hook the actual destruction, which is not a common operation.
    class DestructionObserverProbe : public RefCounted<DestructionObserverProbe> {
    public:
        DestructionObserverProbe(bool* task_destroyed,
            bool* destruction_observer_called)
            : task_destroyed_(task_destroyed)
            , destruction_observer_called_(destruction_observer_called)
        {
        }
        virtual void Run()
        {
            // This task should never run.
            ADD_FAILURE();
        }

    private:
        friend class RefCounted<DestructionObserverProbe>;

        virtual ~DestructionObserverProbe()
        {
            EXPECT_FALSE(*destruction_observer_called_);
            *task_destroyed_ = true;
        }

        bool* task_destroyed_;
        bool* destruction_observer_called_;
    };

    class MLDestructionObserver : public MessageLoop::DestructionObserver {
    public:
        MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called)
            : task_destroyed_(task_destroyed)
            , destruction_observer_called_(destruction_observer_called)
            , task_destroyed_before_message_loop_(false)
        {
        }
        void WillDestroyCurrentMessageLoop() override
        {
            task_destroyed_before_message_loop_ = *task_destroyed_;
            *destruction_observer_called_ = true;
        }
        bool task_destroyed_before_message_loop() const
        {
            return task_destroyed_before_message_loop_;
        }

    private:
        bool* task_destroyed_;
        bool* destruction_observer_called_;
        bool task_destroyed_before_message_loop_;
    };

} // namespace

TEST(MessageLoopTest, DestructionObserverTest)
{
    // Verify that the destruction observer gets called at the very end (after
    // all the pending tasks have been destroyed).
    MessageLoop* loop = new MessageLoop;
    const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);

    bool task_destroyed = false;
    bool destruction_observer_called = false;

    MLDestructionObserver observer(&task_destroyed, &destruction_observer_called);
    loop->AddDestructionObserver(&observer);
    loop->task_runner()->PostDelayedTask(
        FROM_HERE, Bind(&DestructionObserverProbe::Run, new DestructionObserverProbe(&task_destroyed, &destruction_observer_called)),
        kDelay);
    delete loop;
    EXPECT_TRUE(observer.task_destroyed_before_message_loop());
    // The task should have been destroyed when we deleted the loop.
    EXPECT_TRUE(task_destroyed);
    EXPECT_TRUE(destruction_observer_called);
}

// Verify that MessageLoop sets ThreadMainTaskRunner::current() and it
// posts tasks on that message loop.
TEST(MessageLoopTest, ThreadMainTaskRunner)
{
    MessageLoop loop;

    scoped_refptr<Foo> foo(new Foo());
    std::string a("a");
    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, Bind(&Foo::Test1ConstRef, foo.get(), a));

    // Post quit task;
    MessageLoop::current()->task_runner()->PostTask(
        FROM_HERE,
        Bind(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));

    // Now kick things off
    RunLoop().Run();

    EXPECT_EQ(foo->test_count(), 1);
    EXPECT_EQ(foo->result(), "a");
}

TEST(MessageLoopTest, IsType)
{
    MessageLoop loop(MessageLoop::TYPE_UI);
    EXPECT_TRUE(loop.IsType(MessageLoop::TYPE_UI));
    EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_IO));
    EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT));
}

#if defined(OS_WIN)
void EmptyFunction()
{
}

void PostMultipleTasks()
{
    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
}

static const int kSignalMsg = WM_USER + 2;

void PostWindowsMessage(HWND message_hwnd)
{
    PostMessage(message_hwnd, kSignalMsg, 0, 2);
}

void EndTest(bool* did_run, HWND hwnd)
{
    *did_run = true;
    PostMessage(hwnd, WM_CLOSE, 0, 0);
}

int kMyMessageFilterCode = 0x5002;

LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message,
    WPARAM wparam, LPARAM lparam)
{
    if (message == WM_CLOSE)
        EXPECT_TRUE(DestroyWindow(hwnd));
    if (message != kSignalMsg)
        return DefWindowProc(hwnd, message, wparam, lparam);

    switch (lparam) {
    case 1:
        // First, we post a task that will post multiple no-op tasks to make sure
        // that the pump's incoming task queue does not become empty during the
        // test.
        MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&PostMultipleTasks));
        // Next, we post a task that posts a windows message to trigger the second
        // stage of the test.
        MessageLoop::current()->PostTask(FROM_HERE,
            base::Bind(&PostWindowsMessage, hwnd));
        break;
    case 2:
        // Since we're about to enter a modal loop, tell the message loop that we
        // intend to nest tasks.
        MessageLoop::current()->SetNestableTasksAllowed(true);
        bool did_run = false;
        MessageLoop::current()->PostTask(FROM_HERE,
            base::Bind(&EndTest, &did_run, hwnd));
        // Run a nested windows-style message loop and verify that our task runs. If
        // it doesn't, then we'll loop here until the test times out.
        MSG msg;
        while (GetMessage(&msg, 0, 0, 0)) {
            if (!CallMsgFilter(&msg, kMyMessageFilterCode))
                DispatchMessage(&msg);
            // If this message is a WM_CLOSE, explicitly exit the modal loop. Posting
            // a WM_QUIT should handle this, but unfortunately MessagePumpWin eats
            // WM_QUIT messages even when running inside a modal loop.
            if (msg.message == WM_CLOSE)
                break;
        }
        EXPECT_TRUE(did_run);
        MessageLoop::current()->QuitWhenIdle();
        break;
    }
    return 0;
}

TEST(MessageLoopTest, AlwaysHaveUserMessageWhenNesting)
{
    MessageLoop loop(MessageLoop::TYPE_UI);
    HINSTANCE instance = CURRENT_MODULE();
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(wc);
    wc.lpfnWndProc = TestWndProcThunk;
    wc.hInstance = instance;
    wc.lpszClassName = L"MessageLoopTest_HWND";
    ATOM atom = RegisterClassEx(&wc);
    ASSERT_TRUE(atom);

    HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0,
        HWND_MESSAGE, 0, instance, 0);
    ASSERT_TRUE(message_hwnd) << GetLastError();

    ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));

    loop.Run();

    ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
}
#endif // defined(OS_WIN)

TEST(MessageLoopTest, SetTaskRunner)
{
    MessageLoop loop;
    scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());

    loop.SetTaskRunner(new_runner);
    EXPECT_EQ(new_runner, loop.task_runner());
    EXPECT_EQ(new_runner, ThreadTaskRunnerHandle::Get());
}

TEST(MessageLoopTest, OriginalRunnerWorks)
{
    MessageLoop loop;
    scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());
    scoped_refptr<SingleThreadTaskRunner> original_runner(loop.task_runner());
    loop.SetTaskRunner(new_runner);

    scoped_refptr<Foo> foo(new Foo());
    original_runner->PostTask(FROM_HERE,
        Bind(&Foo::Test1ConstRef, foo.get(), "a"));
    RunLoop().RunUntilIdle();
    EXPECT_EQ(1, foo->test_count());
}

TEST(MessageLoopTest, DeleteUnboundLoop)
{
    // It should be possible to delete an unbound message loop on a thread which
    // already has another active loop. This happens when thread creation fails.
    MessageLoop loop;
    std::unique_ptr<MessageLoop> unbound_loop(MessageLoop::CreateUnbound(
        MessageLoop::TYPE_DEFAULT, MessageLoop::MessagePumpFactoryCallback()));
    unbound_loop.reset();
    EXPECT_EQ(&loop, MessageLoop::current());
    EXPECT_EQ(loop.task_runner(), ThreadTaskRunnerHandle::Get());
}

TEST(MessageLoopTest, ThreadName)
{
    {
        std::string kThreadName("foo");
        MessageLoop loop;
        PlatformThread::SetName(kThreadName);
        EXPECT_EQ(kThreadName, loop.GetThreadName());
    }

    {
        std::string kThreadName("bar");
        base::Thread thread(kThreadName);
        ASSERT_TRUE(thread.StartAndWaitForTesting());
        EXPECT_EQ(kThreadName, thread.message_loop()->GetThreadName());
    }
}

} // namespace base
